<?php
/**
 * D5Power Studio d5power Webgame Engine
 *
 * Extends DOTK system
 * Author: D5Power Studio
 *
 *
 * 第五动力工作室 d5power网页游戏引擎 Ver 1.0
 * 继承自:DOTK引擎
 * 作者：第五动力工作室
 */
 
/** PAGE NOT ALLOWED
 * Verify if user called this page through a allowed page.
 * @message: That file cannot be displayed alone.
 */
if (!isset($allowed_page)) { echo "That file cannot be displayed alone."; exit; }

require_once(CLASS_PATH."Player.class.php");
require_once(CLASS_PATH."Character.class.php");
require_once(CLASS_PATH."d5power.DAO.php");

/**
* Class Engine
* Handler of game rules
*/
class Engine extends DAO {
    
  public $player;
  public $map;
  public $character;
  public $battleInstance = null;
  public $d5m;
  
  private $charactersOnScreen = array();
  private $lastChatId = null;
  private $lastEventsCheck = null ;
  private $index_move = null;
    
  /**
  * Engine Constructor function
  * @description initilizes the game core objects.
  */
  public function Engine() {
    $this->player = new Player();
    $this->map = new Map();
    $this->character = new Character();
	$this->battleInstance = new Battle();
  }

	/**
	  * Function leaveGame
	  * @description Used to logout of the game
	  */
	public function leaveGame()
	{
		if($this->character->getBusy())
		{
			echo "您现在暂时不能离开游戏！";
		}else{
			$this->character->offLine();
			session_destroy();
			header("Location:index.php");
		}
	}

  /**
  *
  */
  public function initialize() {
    //nothing
  }
  
  /**
  *
  */
  public function sendChat($message) {
    $query = "insert into chat(cid, mid, chatmsg, chatdate) values(?,?,?,?);";
    $this->retrieve($query, array($this->character->getcharacterId(), $this->map->getMapId(), stripcslashes($message), time()));
    print("OK");
  }

	/**
	*
	*/
	public function characterWalk($x, $y)
	{
		$this->character->setPosx($x);
		$this->character->setPosy($y);	
		$sql = "update characters set posx = ?, posy = ?, lastmove=? where cid = ?";
		@$this->retrieve($sql, array($x, $y, time(), $this->character->getcharacterId()));
		print("OK");
	}


  /**
  * Function loadCharacters
  * @description Load all online characters in your actual map.
  */
  public function loadOtherCharacters()
  {
    try {
      $sql = "SELECT
                cid, charactername, posx, posy, hp,gender,avatar 
              FROM
                characters
              WHERE
                online = 1 and
                mid = ? and 
				cid <> ?";
	  $charactersResult = $this->retrieve($sql, array($this->character->getActualMap(), $this->character->getcharacterId()));
      $this->charactersOnScreen = array();

      for($i=0; i<count($charactersResult->getArray()) && $char = $charactersResult->getRow(); $i++) {
        $this->charactersOnScreen[$i] = new Character();
        $this->charactersOnScreen[$i]->load($char['cid']);
      }
	  echo json_encode($charactersResult->getArray());
    }
    catch(Exception $e) {
     //TODO: correct Exception
    }
  }


  /**
  * Function loadMyCharacters
  * @description Used to retrieve all characters of one Player.
  * @return {ResultSet}
  */
  public function loadMyCharacters() {
    $sql = "SELECT cid, uid, charactername, mid, posx, posy,gender,avatar FROM characters WHERE uid = ?";
    try {
      $characterResult = $this->retrieve($sql, array($_SESSION['playerId']));
      return $characterResult->getArray();
    }
    catch(Exception $e) {
      //TODO
    }
  }


  /**
  *
  */
  public function getMyLoggedCharacter() {
    echo json_encode($this->character);
  }


  /**
  *
  */
  public function getCollisionPoints() {
    $points = $this->map->getCollisionPoints();
    $matrix = Array();
    $charpos = 0;
    for($i=0; $i<30; $i++) {
        $matrix[$i] = Array();
        for($j=0; $j<40; $j++) {
            array_push($matrix[$i], (int) substr($points,$charpos,1));
            $charpos++;
        }
    }
    echo json_encode($matrix);
  }
  

  /**
  * Function loadMap
  * @description 
  */
  public function loadMap($layer) {
    header('Content-type: image/gif');
	header("Cache-Control: no-cache, must-revalidate");
    header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
    $this->map->loadMap($this->character->getMap_id());

    //TODO: Validations about the logged character.
    $fileName = "";
    $mapsPath = "../pictures/maps";
    switch ($layer) {

        case "terrain_layer":
            $fileName = $mapsPath."/".$this->map->getTerrainLayer();
        break;

        case "ground_layer":
            $fileName = $mapsPath."/".$this->map->getGroundLayer();
        break;

        case "up_layer":
            $fileName = $mapsPath."/".$this->map->getUpLayer();
        break;
    }
    $fileName = realpath($fileName);
    $file = fopen($fileName,'r');
    $contents = fread($file, filesize($fileName));
    fclose($file);
    echo $contents;
  }

	/**
	* Function loadWaypoints
	*/
	public function loadWaypoints()
	{
		print(json_encode($this->map->getWaypoints()));
	}

  /**
  * Function loadCharset
  * @description Used to load all charset images of the current character
  */
  public function loadCharset($orientation, $number) {
    header('Contend-type: image/gif');
    $imagesPath = "../pictures/charsets";
	
	if($orientation != "north" &&
	   $orientation != "south" &&
	   $orientation != "west" &&
	   $orientation != "east") {
	   	throw new Exception("Wrong orientation.");
	}	
	
    $fileName = $imagesPath."/".$this->getAvatar()."_".$this->getProfession()."_".$orientation."_".$number.".gif";
    $fileName = realpath($fileName);
    $file = fopen($fileName,'r');
    $contents = fread($file, filesize($fileName));
    fclose($file);
    echo $contents;
  }
  

	/**
	* Function login
	* @description Authentication of players
	*/
	public function login($user, $pwd)
	{
		try
		{
			if($user == "")
			{
				throw new Exception("Please fill in the Username field. Try again");	
				exit;
			}
			if($pwd == "")
			{
				throw new Exception("Please fill in the password field. Try again");
				exit;
			}  	
			if(stripos($user,"\'") != false || stripos($pwd,"\'") != false)
			{
				throw new Exception("Invalid Characters detected. Please check your username.");
				exit;
			}
			$query = "select uid, userid, passwd from user_login ";
			$query.= "where userid = ? and passwd   = ?";
			
			$results = $this->retrieve($query, array($user, $pwd));
			if(!$results->isEmpty())
			{
				$results = $results->getRow();
				//If the username and password are correct, we initialize the Player
				$this->player->load($results['uid']);
				$_SESSION['playerId'] = $this->player->getId();
				return true;
			}else{
				throw new Exception("Unable to login please check your Username and Password.");
			}    
		}catch(Exception $e){
			return $e->getMessage();
		}
	}

  /**
  * Function setCharacter
  * @description Used when a player login in the game
  */
  public function setCharacter($characterId) {
    $this->character = new Character();
    $this->character->load($characterId);
  }
  
  
  /**
  * Function enterGame
  * @description
  */
  function enterGame($characterId) {
    $this->character = new Character();
    $this->character->load($characterId);
	
	$checktime=time()-AUTO_OFFLINE_TIME;
	if($this->character->getOnline()==1 && $this->character->getLastmove()>$checktime)
	{
		die('该角色目前正在使用，点<a href="index.php">这里返回</a>');
	}
    $this->character->setOnline(1);
    $this->character->save();
	$this->character->offlineMyOtherCharacter(); // 将本用户其他角色下线
	$this->d5m = new D5Memcache();
    return true;
  }
  
  /**
  * Function getNewEvents
  * @description
  * @return {Json} package
  */
  public function getNewEvents() {
    
    $responseArray = array("chat"=>null, "movements"=>null, "entermap"=>array(), "outmap"=>array(), "battle_event"=>array());

    //////////Check new chat messages
    if ($this->lastChatId == null) {
      $sql = "select id, cid, chatmsg, mid from chat where mid = ? and cid <> ? and chatdate > ? order by id asc";
      $messages_result = $this->retrieve($sql, array($this->map->getMapId(), $this->character->getcharacterId(), time()));
    }
    else {
      $sql = "select id, cid, chatmsg, mid from chat where id > ? and mid = ? and cid <> ? order by id asc";
      $messages_result = $this->retrieve($sql, array($this->lastChatId, $this->map->getMapId(), $this->character->getcharacterId()));
    }
    if(!$messages_result->isEmpty()) {
        $chatId = $messages_result->getLast();
        $this->lastChatId = $chatId["id"];
        $responseArray["chat"] = $messages_result->getArray();
    }
    /////////////////////////////////
	

	//Check for character movements
	if($this->index_move == null) {
		$this->index_move = 0;		
	}
	$sql = "select
	          lastmove,
	          cid,
			  charactername,
			  profession,
              avatar,
			  posx,
			  posy,
			  mid,
			  online,
			  avatar,
			  gender
			from
			  characters
			where
			  mid = ? and
			  online = 1 and
			  lastmove > ? and
			  cid <> ?";
	$characters = $this->retrieve($sql, array($this->character->getActualMap(), $this->index_move, $this->character->getcharacterId()));
	if(!$characters->isEmpty()) {
		$index = $characters->getFirst();
		$this->index_move = $index["last_move"];
		$responseArray["movements"] = $characters->getArray();
	}
	/////////////////////////////////
	
	/////////////////////////////////

    //Check for new characters entered the map
    //TODO: too long rotine, maybe need to improve with array_diff_ukey php function
	$characters->reset();
	$entermap = array();
	for($i=0; $i<count($characters->getArray()); $i++)
	{
		$char = $characters->getRow();
		$encontered = false;
		for($j=0; $j<count($this->charactersOnScreen); $j++)
		{
			if($char["cid"] == $this->charactersOnScreen[$j]->getcharacterId())
			{
	  			$encontered = true;
	  			break;
			}
		}
	
		if(!$encontered)
		{
			//Add the new character to response array.
			array_push($entermap,$char);
			//Add the new character to server character's list.
			$new_char = new Character();
			$new_char->load($char["cid"]);
			array_push($this->charactersOnScreen, $new_char);
		}
	}
	$responseArray["entermap"] = $entermap;
	///////////////////////////////

    //Check if anybody leaves the current map.
    //TODO: too long rotine, maybe need to improve with array_diff_ukey php function
	$outmap = array();
    for($i=0; $i<count($this->charactersOnScreen); $i++)
	{
		$characters->reset();
		for($j=0; $j<count($characters->getArray()); $j++)
		{
			$char = $characters->getRow();
			$encontered = false;
			if($this->charactersOnScreen[$i]->getcharacterId() == $char["cid"])
			{
				$encontered = true;
				break;
			}
		}
		
		if(!$encontered)
		{
			//Add the character to be removed
			array_push($outmap, $this->charactersOnScreen[$i]->getcharacterId());
			//Remove the character from server character's list.
			array_splice($this->charactersOnScreen,$i,1);
		}
    }
	
	$responseArray["outmap"] = $outmap;
	
	////////////////////////////////

    //Create Json output
    echo json_encode($responseArray);
  }
	
	
	/**
	* Function changeMap
	* @description
	*/
	public function changeMap()
	{
	$sql = "select
			  mid_to,
			  posx_to,
			  posy_to
			from
			  waypoints
			where
			  mid_from = ? and
			  posx_from = ? and
			  posy_from = ?";
	try {
	  $waypoint = $this->retrieve($sql, array($this->character->getMap_id(),
	  $this->character->getPosx(),
	  $this->character->getPosy()));
	}
	catch(Exception $e) {
	  print($e->getMessage());
	}
	
	if(isset($waypoint) && !$waypoint->isEmpty()) {
	  $waypoint = $waypoint->getRow();
	  $this->character->setMap_id($waypoint['mid_to']);
	  $this->character->setPosx($waypoint['posx_to']);
	  $this->character->setPosy($waypoint['posy_to']);
	
	  //$sql = "update characters set posx = ?, posy = ?, map_id = ? where character_id = ?";
	  //$this->retrieve($sql, array($waypoint['posx_to'], $waypoint['posy_to'], $waypoint['map_id_to'], $this->character->character_id));
	  $this->character->save();
	  echo "OK";
	}else {
	  echo "wrong_waypoint";
	}
	}
	
	/**
	 *	loadNpcEvent
	 *	NPC读取
	 *
	 */
	
	public function loadNpc()
	{
		$sql = "select npc_id,npc_name,posx,posy,avatar from npc where mid=?";
		try
		{
			$npc = $this->retrieve($sql, array($this->character->getMap_id()));
		}catch(Exception $e){
			print($e->getMessage());
		}
		
		if(isset($npc) && !$npc->isEmpty())
		{
			echo json_encode($npc->getArray());
		}else {
		  echo "Not found any npc";
		}
	}
	/**
	 *	loadNpcEvent
	 *	NPC事件读取
	 *
	 */
	
	public function loadNpcEvent($npcid)
	{
		$sql = "select npc_id,info,callbackname,callbackfunction from npc_events where npc_id=?";
		try
		{
			$npcEvent = $this->retrieve($sql, array(intval($npcid)));
		}catch(Exception $e){
			print($e->getMessage());
		}
		
		if(isset($npcEvent) && !$npcEvent->isEmpty())
		{
			echo json_encode($npcEvent->getRow());
		}else {
		  echo "wrong_waypoint";
		}
	}
	
	/**
	 * 物品系统
	 *
	 *
	 *
	 */
	public function loadPackage()
	{
		
		$packageList = $this->character->myPackage();
		echo json_encode($packageList);
	}
	
	
	public function useItem($item_id)
	{
		$item_id=intval($item_id);
		if($item_id==0) die();
		$result = $this->character->useItem($item_id);
		
		if(is_array($result))
		{
			echo json_encode($result);
		}else{
			echo $result;
		}
	}
	
	/* ------ 装备系统 ------ */
	public function loadEquipment()
	{
		$sql = 'select e.cid,e.item_id,e._where,i.id,i._name,i._info ';
		$sql.= 'from character_equipment as e LEFT JOIN ';
		$sql.= 'item as i ON (e.item_id=i.id) ';
		$sql.= 'where e.cid=?';
		
		try
		{
			$equ = $this->retrieve($sql, array($this->character->getCharacterId()));
		}catch(Exception $e){
			print($e->getMessage());
		}
		
		if(isset($equ) && !$equ->isEmpty())
		{
			echo json_encode($equ->getD5Array());
		}else {
			echo 'NONE';
		}
	}
	/**
	 * 魔物读取
	 * 
	 */
	public function loadMonster()
	{
		$sql = "select l.monster_id,l.mid,l._num,l._relive,";
		$sql.= "m.monster_id,m._name,m._str,m._dex,m._agi,m._int,m._vit,m._level,m._atk,m._def,m._avatar ";
		$sql.= "from monster_in_map as l LEFT JOIN monsters as m ON (l.monster_id=m.monster_id) ";
		$sql.= "where l.mid=?";
		
		try
		{
			$monster = $this->retrieve($sql, array($this->character->getMap_id()));
		}catch(Exception $e){
			print($e->getMessage());
		}
		
		if(isset($monster) && !$monster->isEmpty())
		{
			echo json_encode($monster->getArray());
		}else {
		  echo "wrong_waypoint";
		}
	}
	
	/**
	 *	魔物战斗
	 *	
	 *	
	 */
	public function monsterFightEnd($rival)
	{
		if($rival==0) die();
		//获取怪物经验
		$sql = "select monster_id,_exp from monsters where monster_id=?";
		$monster = $this->retrieve($sql, array($rival));
		
		$result='';
		if(!$monster->isEmpty())
		{
			$monster = $monster->getRow();
			// 增加经验值
			$exp = $monster['_exp'];
			if($exp>0)
			{
				$exp=$exp*EXP_BEI;
				$this->character->gainXp($exp);
				$lvup = $this->character->isLevelUp();
			}
			
			
			$result = '获得'.$exp.'点经验值';
			
			// 获取掉落物品
			$sql = "select monster_id,item_id,_persent from monster_fall_item where monster_id=? order by _persent";
			$fall_down = $this->retrieve($sql, array($rival));
			
			$fall_item=0;
			if(!$fall_down->isEmpty())
			{
				for($i=0;$i<count($fall_down->getArray());$i++)
				{
					$this_item = $fall_down->getRow();
					$per = 100*$this_item['_persent'];
					$rand = rand(0,10000);
					if($rand<$per)
					{
						$fall_item = $this_item['item_id'];
						break;
					}
				}
			}

			if($fall_item>0)
			{
				$sql = 'select id,_name from item where id=?';
				$db = $this->retrieve($sql, array($fall_item));
				$item = $db->getRow();
				
				switch($this->character->getItem($item['id']))
				{
					case 'OK':
						
						$item_event = '';
						break;
					
					case 'FULL':
						
						$item_event = '。但您的背包满了，'.$item['_name'].'被丢掉了！';
						$fall_item=0;
						break;
					
					case 'ITEMFULL':
						
						$item_event = '。但您的背包里放了太多的'.$item['_name'].'，实在装不下了。';
						$fall_item=0;
						break;
					
					case 'FULLOK':
						
						$item_event = '。但您的背包里放了不少的'.$item['_name'].'，只装下了几个。';
						break;
					
					default:
						break;
				}
				
				$result.='<br>获得了'.$item['_name'].$item_event;
			}
			
			$result.='<br>5秒后自动退出战斗场景';
			$fight_arr = array(
				'msg'		=>	$result,
				'item_id'	=>	$fall_item,
				'xp'		=>	$exp,
				'lvup'		=>	$lvup,
			);
			echo json_encode($fight_arr);
		}else{
			die();
		}
		
	}
	
	public function iAmDie()
	{
		$this->character->Death();
		$result='<font color="#ff0000">胜败乃兵家常事，大侠请重新来过！</font><br>5秒后自动退出战斗场景';
		$fight_arr = array(
				'msg'		=>	$result,
				'goHome'	=>	'Y',
			);
		echo json_encode($fight_arr);
	}
	
   /**
	*
	*/
	public function inviteToBattle($opponent_id)
	{
	
		if($this->battleInstance->inFight())
		{
			throw new Exception("您正在战斗中");
			return false;
		}
		$opponent = null; // 被挑战方
		for($i=0; $i<count($this->charactersOnScreen); $i++)
		{
			if($this->charactersOnScreen[$i]->getcharacterId() == $opponent_id)
			{
				$opponent = $this->charactersOnScreen[$i];
				break;
			}
			//echo $this->charactersOnScreen[$i]->getcharacterId().":".$opponent_id;
		}
		
		if(is_null($opponent))
		{
			throw new Exception("您要挑战的人不在线。");
			return false;
		}else{
			$this->battleInstance->create($this->character, $opponent);
			return true;
		}
	}

	public function acceptBattle()
	{
		if($this->battleInstance->getStatus() == "WAITING ACCEPTANCE")
		{
			$this->battleInstance->acceptBattle();
		}
	}

	public function rejectBattle()
	{
		if($this->battleInstance->getState() == "WAITING ACCEPTANCE")
		{
			$this->battleInstance->recuseBattle();
			$this->battleInstance = new Battle();
		}
	}

	public function attack()
	{
		if($this->battleInstance->inFight())
		{
			$battle_msg = $this->battleInstance->attack();
			echo json_encode($battle_msg);
		}
	}
  public function debug() {
    print(date("d\.m\.Y H\:i\:s"));
  }

}
?>